package com.zhu.instructions.references;

import com.zhu.instructions.base.Index16Instruction;
import com.zhu.rtda.Frame;
import com.zhu.rtda.InstanceOop;
import com.zhu.rtda.OperandStack;
import com.zhu.rtda.Slot;
import com.zhu.rtda.heap.Field;
import com.zhu.rtda.heap.InstanceKlass;
import com.zhu.rtda.heap.Method;
import com.zhu.rtda.heap.constant.FieldRef;
import com.zhu.rtda.heap.constant.RuntimeConstantPool;

public class PUT_FIELD extends Index16Instruction {
    @Override
    public void execute(Frame frame) {
        // 1. 获取当前方法信息（用于 final 检查）
        Method currentMethod = frame.getMethod();
        InstanceKlass currentClass = currentMethod.getClazz();
        RuntimeConstantPool cp = currentClass.getConstantPool();

        // 2. 解析 FieldRef
        FieldRef fieldRef = (FieldRef) cp.getConstant(this.index);
        Field field = fieldRef.resolvedField();

        // 3. 必须是非静态字段
        if (field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        // 4. final 字段检查：只能在定义类的 <init> 方法中赋值
        if (field.isFinal()) {
            InstanceKlass fieldClass = field.getClazz();
            if (currentClass != fieldClass || !"<init>".equals(currentMethod.getName())) {
                throw new IllegalAccessError();
            }
        }

        // 5. 获取字段信息
        String descriptor = field.getDescriptor();
        int slotId = field.getSlotId();
        OperandStack stack = frame.getOperandStack();

        // 6. 根据类型写入值（注意：先 pop 值，再 pop 对象引用）
        switch (descriptor.charAt(0)) {
            case 'Z': case 'B': case 'C': case 'S': case 'I': {
                int val = stack.popInt();
                InstanceOop ref = stack.popRef();
                if (ref == null) throw new NullPointerException();
                ref.getFields()[slotId].setNum(val);
                break;
            }
            case 'F': {
                float val = stack.popFloat();
                int bits = Float.floatToIntBits(val);
                InstanceOop ref = stack.popRef();
                if (ref == null) throw new NullPointerException();
                ref.getFields()[slotId].setNum(bits);
                break;
            }
            case 'J': {
                long val = stack.popLong();
                int low = (int) val;
                int high = (int) (val >>> 32);
                InstanceOop ref = stack.popRef();
                if (ref == null) throw new NullPointerException();
                Slot[] fields = ref.getFields();
                fields[slotId].setNum(low);
                fields[slotId + 1].setNum(high);
                break;
            }
            case 'D': {
                double val = stack.popDouble();
                long bits = Double.doubleToLongBits(val);
                int low = (int) bits;
                int high = (int) (bits >>> 32);
                InstanceOop ref = stack.popRef();
                if (ref == null) throw new NullPointerException();
                Slot[] fields = ref.getFields();
                fields[slotId].setNum(low);
                fields[slotId + 1].setNum(high);
                break;
            }
            case 'L': case '[': {
                InstanceOop val = stack.popRef();
                InstanceOop ref = stack.popRef();
                if (ref == null) throw new NullPointerException();
                ref.getFields()[slotId].setRef(val);
                break;
            }
            default:
                throw new RuntimeException("Unsupported field type: " + descriptor);
        }
    }
}
